package com.example.framework509.common.services.Impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.framework509.common.annotation.Remind;
import com.example.framework509.common.annotation.entityAnnotations.Primary;
import com.example.framework509.common.annotation.entityAnnotations.Secondary;
import com.example.framework509.common.entities.MapperParam;
import com.example.framework509.common.exceptions.JdbcException;
import com.example.framework509.common.exceptions.NotExistException;
import com.example.framework509.common.services.Impl.base.BaseReflectionServiceImpl;
import com.example.framework509.common.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

@SuppressWarnings("unchecked")
public abstract class AutoMapperServiceImpl<E, PRIMARY_CLASS, SECONDARY_CLASS> extends BaseReflectionServiceImpl<E> {

    @Remind("@Primary @Secondary 来标记主副id")
    @Autowired
    public BaseMapper<E> mapper;
    @Autowired
    protected AutoEntityServiceImpl<PRIMARY_CLASS> primaryEntityBaseService;
    @Autowired
    protected AutoEntityServiceImpl<SECONDARY_CLASS> secondaryEntityBaseService;

    @Autowired
    protected RedisUtil redisUtil;

    protected final Class<?> ENTITY_CLASS = getEntityClass();
    protected final String ENTITY_NAME = ENTITY_CLASS.getSimpleName();
    protected final String ENTITY_NAME_LOWER = Character.toLowerCase(ENTITY_NAME.charAt(0)) + ENTITY_NAME.substring(1);
    protected final String PRIMARY_FIELD_NAME = getPrimary();
    protected final String SECONDARY_FIELD_NAME = getSecondary();

    public void addMapperFetchTypeObj(Object primaryIdObj, List<Object> secondaryIdsObj) throws Exception {
        Integer primaryId = (Integer) primaryIdObj;
        List<Integer> secondaryIds = (List) secondaryIdsObj;
        addMapperFetch(primaryId, secondaryIds);
    }

    public void addMapperFetch(Integer primaryId, List<Integer> secondaryIds) throws Exception {
        E jdbcEntity;
        getAllEntityNum();
        try {
            PRIMARY_CLASS entityPrimary = primaryEntityBaseService.getEntityFromId(primaryId, true);
            if (entityPrimary == null) throw new JdbcException("添加的关联Primary：" + primaryId + "实体或已被其他用户删除");
            for (Integer secId : secondaryIds) {
                jdbcEntity = (E) ENTITY_CLASS.getDeclaredConstructor().newInstance();
                setFieldValue(jdbcEntity, PRIMARY_FIELD_NAME, primaryId);
                setFieldValue(jdbcEntity, SECONDARY_FIELD_NAME, secId);
                SECONDARY_CLASS entitySecondary = secondaryEntityBaseService.getEntityFromId(secId, true);
                if (entitySecondary == null) throw new JdbcException("添加的关联Secondary:" + secId + "实体或已被其他用户删除");
                if (hasSameMapper(primaryId, secId)) continue;
                int state = mapper.insert(jdbcEntity);
                if (state != 0) addMapperInRedisCallback(new MapperParam(primaryId, secId));
                else throw new JdbcException(getEntityClass().getName() + "数据插入异常,详情:P:" + primaryId + "S:" + secId);
            }
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            throw e;
        }
    }

    public void addPriMapperFetch(Integer secondaryId, List<Integer> primaryIds) throws Exception {
        E jdbcEntity;
        getAllEntityNum();
        try {
            SECONDARY_CLASS entitySecondary = secondaryEntityBaseService.getEntityFromId(secondaryId, true);
            if (entitySecondary == null) throw new JdbcException("添加的关联Secondary:" + secondaryId + "实体或已被其他用户删除");
            for (Integer priId : primaryIds) {
                jdbcEntity = (E) ENTITY_CLASS.getDeclaredConstructor().newInstance();
                setFieldValue(jdbcEntity, PRIMARY_FIELD_NAME, priId);
                setFieldValue(jdbcEntity, SECONDARY_FIELD_NAME, secondaryId);
                PRIMARY_CLASS entityPrimary = primaryEntityBaseService.getEntityFromId(priId, true);
                if (entityPrimary == null) throw new JdbcException("添加的关联Primary:" + priId + "实体或已被其他用户删除");
                if (hasSameMapper(priId, secondaryId)) continue;
                int state = mapper.insert(jdbcEntity);
                if (state != 0) addMapperInRedisCallback(new MapperParam(priId, secondaryId));
                else throw new JdbcException(getEntityClass().getName() + "数据插入异常,详情:P:" + priId + "S:" + secondaryId);
            }
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            throw e;
        }
    }

    public void addMapper(MapperParam param) throws Exception {
        if (param.getPrimaryId() == null || param.getSecondaryId() == null)
            throw new NotExistException("MapperParam 传参异常");
        E jdbcEntity;
        getAllEntityNum();
        try {
            jdbcEntity = (E) ENTITY_CLASS.getDeclaredConstructor().newInstance();
            //getPrimary getSecondary 已经处理过未注解异常所以不用判断
            setFieldValue(jdbcEntity, PRIMARY_FIELD_NAME, param.getPrimaryId());
            setFieldValue(jdbcEntity, SECONDARY_FIELD_NAME, param.getSecondaryId());
            if (param.getCreator() != null)
                setFieldValue(jdbcEntity, "creator", param.getCreator());

            PRIMARY_CLASS entityPrimary = primaryEntityBaseService.getEntityFromId(param.getPrimaryId(), true);
            SECONDARY_CLASS entitySecondary = secondaryEntityBaseService.getEntityFromId(param.getSecondaryId(), true);
            if (entityPrimary == null || entitySecondary == null)
                throw new JdbcException("添加的关联Primary：" + param.getPrimaryId() + ",Secondary:" + param.getSecondaryId() + "实体或已被其他用户删除");

            if (hasSameMapper(param.getPrimaryId(), param.getSecondaryId()))
                return;

            int state = mapper.insert(jdbcEntity);
            if (state != 0) addMapperInRedisCallback(param);
            else
                throw new JdbcException(getEntityClass().getName() + "数据插入异常,详情:P:" + param.getPrimaryId() + "S:" + param.getSecondaryId());
        } catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException e) {
            throw e;
        }
    }

    //需要验重及映射类型校验，比如1:1时不应有更多的映射关系
    protected abstract boolean hasSameMapper(Integer primaryId, Integer secondaryId);

    public void delMapperFetchTypeObj(Object primaryIdObj, List<Object> secondaryIdsObj) throws Exception {
        Integer primaryId = (Integer) primaryIdObj;
        List<Integer> secondaryIds = (List) secondaryIdsObj;
        delMapperFetch(primaryId, secondaryIds);
    }

    public void delMapperFetch(Integer primaryId, List<Integer> secondaryIds) throws Exception {
        for (Integer secId : secondaryIds) {
            QueryWrapper<E> wrapper = new QueryWrapper<>();
            wrapper.eq(underscoreName(PRIMARY_FIELD_NAME), primaryId)
                    .eq(underscoreName(SECONDARY_FIELD_NAME), secId);
            E mapperEntity = mapper.selectOne(wrapper);
            if (mapperEntity == null) throw new JdbcException("删除的映射关系不存在");
            int state = mapper.delete(wrapper);
            if (state != 0) delMapperInRedisCallback(new MapperParam(primaryId, secId), primaryId);
            else throw new JdbcException(getEntityClass().getName() + "数据删除异常");
        }
    }

    public void delPriMapperFetch(Integer secondaryId, List<Integer> primaryIds) throws Exception {
        for (Integer priId : primaryIds) {
            QueryWrapper<E> wrapper = new QueryWrapper<>();
            wrapper.eq(underscoreName(PRIMARY_FIELD_NAME), priId)
                    .eq(underscoreName(SECONDARY_FIELD_NAME), secondaryId);
            E mapperEntity = mapper.selectOne(wrapper);
            if (mapperEntity == null) throw new JdbcException("删除的映射关系不存在");
            int state = mapper.delete(wrapper);
            if (state != 0) delMapperInRedisCallback(new MapperParam(priId, secondaryId), priId);
            else throw new JdbcException(getEntityClass().getName() + "数据删除异常");
        }
    }

    public void delMapper(MapperParam param) {
        if (param.getPrimaryId() == null || param.getSecondaryId() == null)
            throw new NotExistException("MapperParam 传参异常");
        QueryWrapper<E> wrapper = new QueryWrapper<>();
        wrapper.eq(underscoreName(PRIMARY_FIELD_NAME), param.getPrimaryId())
                .eq(underscoreName(SECONDARY_FIELD_NAME), param.getSecondaryId());
        E mapperEntity = mapper.selectOne(wrapper);

        int state = mapper.delete(wrapper);
        if (state != 0) delMapperInRedisCallback(param, Integer.parseInt(getFieldValue(mapperEntity, "id").toString()));
        else throw new JdbcException(getEntityClass().getName() + "数据删除异常");
    }

    protected abstract void addMapperInRedis(MapperParam param);

    protected abstract void delMapperInRedis(MapperParam param);

    public void editSecFetchObj(Object primaryIdObj, List<Object> secondaryIdsObj) throws Exception {
        Integer primaryId = (Integer) primaryIdObj;
        List<Integer> secondaryIds = (List) secondaryIdsObj;
        editSecFetchCallBack(primaryId, secondaryIds);
    }

    public void editPriFetchObj(Object secondaryIdObj, List<Object> primaryIdsObj) throws Exception {
        Integer secondaryId = (Integer) secondaryIdObj;
        List<Integer> primaryIds = (List) primaryIdsObj;
        editSecFetchCallBack(secondaryId, primaryIds);
    }

    public void editSecFetch(Integer primaryId, List<Integer> secondaryIds) throws Exception {
        editSecFetchCallBack(primaryId, secondaryIds);
    }

    public void editPriFetch(Integer secondaryId, List<Integer> primaryIds) throws Exception {
        editPriFetchCallBack(secondaryId, primaryIds);
    }

    protected abstract void editSecFetchCallBack(Integer primaryId, List<Integer> secondaryIds) throws Exception;

    protected abstract void editPriFetchCallBack(Integer secondaryId, List<Integer> primaryIds) throws Exception;

    public void addMapperInRedisCallback(MapperParam param) {
        addMapperInRedis(param);
        QueryWrapper<E> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(underscoreName(PRIMARY_FIELD_NAME), param.getPrimaryId())
                .eq(underscoreName(SECONDARY_FIELD_NAME), param.getSecondaryId());
        E mapperEntity = mapper.selectOne(queryWrapper);
        redisUtil.sSet(ENTITY_NAME_LOWER + "Ids", getFieldValue(mapperEntity, "id"));
    }

    public void delMapperInRedisCallback(MapperParam param, Integer id) {
        delMapperInRedis(param);
        redisUtil.setRemove(ENTITY_NAME_LOWER + "Ids", id);
    }

    public Integer getAllEntityNum() {
        Integer numInSQL = mapper.selectCount(null);
        Integer numInRedis = redisUtil.sGet(ENTITY_NAME_LOWER + "Ids").size();
        if (numInRedis.equals(numInSQL)) return numInRedis;
        else {
            return getAllEntitiesToRedis().size();
        }
    }

    private List<E> getAllEntitiesToRedis() {
        List<E> entities = mapper.selectList(null);
        redisUtil.getKeysBeginWith(ENTITY_NAME_LOWER + "*").forEach(redisUtil::del);
        for (E entity : entities) {
            Integer primaryValue = Integer.parseInt(getFieldValue(entity, PRIMARY_FIELD_NAME).toString());
            Integer secondaryValue = Integer.parseInt(getFieldValue(entity, SECONDARY_FIELD_NAME).toString());
            MapperParam mapperParam = new MapperParam(primaryValue, secondaryValue);

            addMapperInRedisCallback(mapperParam);
        }
        return entities;
    }

    private String getPrimary() {
        Field[] declaredFields = ENTITY_CLASS.getDeclaredFields();
        for (Field field : declaredFields) {
            Primary primaryFieldAnnotation = field.getDeclaredAnnotation(Primary.class);
            if (primaryFieldAnnotation != null) return field.getName();
        }
        throw new NullPointerException("@Primary 注解异常");
    }

    private String getSecondary() {
        Field[] declaredFields = ENTITY_CLASS.getDeclaredFields();
        for (Field field : declaredFields) {
            Secondary secondaryFieldAnnotation = field.getDeclaredAnnotation(Secondary.class);
            if (secondaryFieldAnnotation != null) return field.getName();
        }
        throw new NullPointerException("@Secondary 注解异常");
    }

    protected List<Integer> computeSubtraction(List<Integer> target, List<Integer> data) {
        List<Integer> a = new ArrayList<>(target);
        List<Integer> b = new ArrayList<>(data);
        a.removeAll(b);
        return a;
    }
}
